<?php
//error_reporting(E_ALL ^ E_NOTICE);//TMP


if ( ! defined('EXT') ) exit('Invalid file request');

/**
* Manages extension activation, deactivation
*/

class Nce_ldap {
	
	/**
	* Extension settings
	* @var array
	*/
	var $settings			= array();

	/**
	* Extension name
	* @var string
	*/
	var $name				= 'LDAP authentication';

	/**
	* Extension version
	* @var string
	*/
	var $version			= '1.0.0';

	/**
	* Extension description
	* @var string
	*/
	var $description		= 'Handles LDAP login/account creation';

	/**
	* If $settings_exist = 'y' then a settings page will be shown in the ExpressionEngine admin
	* @var string
	*/
	var $settings_exist		= 'y';

	/**
	* Link to extension documentation
	* @var string
	*/
	var $docs_url = 'http://code.google.com/p/ee-ldap-extension/wiki/Introduction';

	/**
	* Debug
	* @var array
	*/
	var $debug = FALSE;
//	var $debug = TRUE;

	var $admin_email = "aglover@ncsoft.com";
	var $mail_host = "ncbpostal";
	var $ldap_host = "ldap://ncbdc2";	
	var $ldap_port = "389";
	var $ldap_search_base = "ou=Users,ou=_UK Office,dc=ncbrighton,dc=nceu";
	var $ldap_search_user = "cn=LDAP GRAILS,ou=SERVICE ACCOUNTS,dc=ncbrighton,dc=nceu";
	var $ldap_search_password = "Password1";
	var $ldap_username_attribute = "sAMAccountName";

	/**
	* PHP4 Constructor
	*
	* @see __construct()
	*/
	function Nce_ldap($settings='')
	{
		$this->__construct($settings);
	}

	/**
	* PHP5 Constructor
	*
	* @param	array|string $settings Extension settings associative array or an empty string
	*/
	function __construct($settings='')
	{
		global $SESS;
		$this->settings = $settings;
				
		if (!isset($this->settings['use_ldap_authentication']) || !$this->settings['use_ldap_authentication'])
		{
			$this->settings['use_ldap_account_creation'] = '0';
		}

		if (!isset($this->settings['use_ldap_account_creation']) || $this->settings['use_ldap_account_creation'])
		{
			$this->settings['use_ldap_authentication'] = '1';
		}
	}


	/**
	* Configuration for the extension settings page
	*
	* @return	array The settings array
	*/
	function settings()
	{
		$settings = array();
		$settings['use_ldap_authentication'] = array('s', array('0' => 'No', '1' => 'Yes'), '1');
		$settings['use_ldap_account_creation'] = array('s', array('0' => 'No', '1' => 'Yes'), '1');
		
		$settings['admin_email'] = $this->admin_email;
		$settings['mail_host'] = $this->mail_host;
		$settings['ldap_host'] = $this->ldap_host;
		$settings['ldap_port'] = $this->ldap_port;
		$settings['ldap_search_base'] = $this->ldap_search_base;
		$settings['ldap_search_user'] = $this->ldap_search_user;
		$settings['ldap_search_password'] = $this->ldap_search_password;
		$settings['ldap_username_attribute'] = $this->ldap_username_attribute;

		return $settings;
	}



	/**
	* Activates the extension
	* 
	* @return	bool Always TRUE
	*/
	function activate_extension ()
	{
		global $DB, $PREFS;

		$default_settings = serialize(
			array(
				'admin_email' => $this->admin_email,
				'mail_host' => $this->mail_host,
				'use_ldap_authentication' => 1,
				'use_ldap_account_creation' => 1,
				'ldap_host' => $this->ldap_host,
				'ldap_port' => $this->ldap_port,
				'ldap_search_base' => $this->ldap_search_base,
				'ldap_search_user' => $this->ldap_search_user,
				'ldap_search_password' => $this->ldap_search_password,
				'ldap_username_attribute' => $this->ldap_username_attribute,
			)
		);

		$hooks = array(
			'login_authenticate_start'		 => 'login_authenticate_start',
			'member_member_login_start'		 => 'member_member_login_start',
		);

		foreach ($hooks as $hook => $method)
		{
			$sql[] = $DB->insert_string( 'exp_extensions', 
											array('extension_id' 	=> '',
												'class'			=> get_class($this),
												'method'		=> $method,
												'hook'			=> $hook,
												'settings'		=> $default_settings,
												'priority'		=> 10,
												'version'		=> $this->version,
												'enabled'		=> "y"
											)
										);
		}
				
		// run all sql queries
		foreach ($sql as $query)
		{
			$DB->query($query);
		}
		
		return TRUE;
	}



	/**
	* Updates the extension
	*/
	function update_extension($current = '')
	{
		global $DB;
		if ($current == '' OR $current == $this->version) return FALSE;
		
		$sql[] = "UPDATE exp_extensions SET version = '" . $DB->escape_str($this->version) . "' WHERE class = '" . get_class($this) . "'";

		// run all sql queries
		foreach ($sql as $query)
		{
			$DB->query($query);
		}
	}



	/**
	* Disables the extension
	*
	* Generally this method would activate the extension however this 
	* automatically happens when we activate the module. As a result this method
	* displays an error message to the user.
	*
	* @return	bool Always TRUE
	*/
	function disable_extension()
	{
		global $DB;
		$DB->query("DELETE FROM exp_extensions WHERE class = '" . get_class($this) . "'");
	}

	function member_member_login_start() // from normal member login
	{
		return $this->login_authenticate_start();
	}
	
	/**
	*/
	function login_authenticate_start() // from control panel login
	{
		global $IN, $OUT, $SESS, $LANG, $LOC, $STAT, $PREFS, $DB, $FNS;
		
		$output = "";
		
		$given_username = $_REQUEST['username'];
		$given_password = $_REQUEST['password'];
		
		if ($this->settings['use_ldap_authentication'] || $this->settings['use_ldap_account_creation'])
		{
			$this->debug_print("Connecting to LDAP...");
			
			$conn = ldap_connect($this->settings['ldap_host'], $this->settings['ldap_port']) or 
				die("Could not connect to host: {$this->settings['ldap_host']}:{$this->settings['ldap_port']}<br/>\n");

			$this->debug_print("connect result is $conn");

			if (empty($this->settings['ldap_search_user']))
			{
				$this->debug_print("Binding anonymously..."); 
				$success = ldap_bind($conn);     // this is an "anonymous" bind, typically read-only access
			}
			else
			{
				$this->debug_print("Binding with user: {$this->settings['ldap_search_user']}..."); 
				$success = ldap_bind($conn, $this->settings['ldap_search_user'], $this->settings['ldap_search_password']); // bind with credentials
			}

			$this->debug_print("Bind result is $success");			
			$this->debug_print("Searching for attribute {$this->settings['ldap_username_attribute']}=$given_username...");				

			// Search username entry
			$result = ldap_search($conn, $this->ldap_search_base, "{$this->settings['ldap_username_attribute']}=$given_username"); 
			$this->debug_print("Search result is: $result");

			if ($result === FALSE)
			{
				exit("Search for username '$given_username' failed<br/>\n");
			}

			$this->debug_print("Number of entires returned is " . ldap_count_entries($conn, $result) . "");

			if (ldap_count_entries($conn, $result) < 1) // username not found, so exit
			{
				exit("Could not find username '$given_username' with LDAP<br/>\n");
			}

			$this->debug_print("Getting entries for '$given_username'...");

			$info = ldap_get_entries($conn, $result); // entry for username found in directory, retrieve entries
			$user_info = $info[0];
			
			$this->debug_print("Data for " . $info["count"] . " items returned<br/>");
/*			
			if ($this->debug) // output all details for entries
			{
				for ($i = 0; $i < $info["count"]; $i++) 
				{
					foreach ($info[$i] as $key => $value)
					{
						$this->debug_print("$key is ", "");
						$this->debug_print($value);
					}
				}
			}
*/
			// authenticate LDAP user against password submitted on login		
			$dn = $user_info['dn'];
			$success = @ldap_bind($conn, $dn, $given_password); // bind with user credentials

			if (!$success) 
			{
				$this->debug_print("Error binding with supplied password (dn: $dn) ERROR: " . ldap_error($conn));
			} 
			
			$is_authenticated = ($success === TRUE);
			
			if ($is_authenticated) // details match in LDAP, update password in exp_members to match (if account exists)
			{							
				$encrypted_password = $FNS->hash(stripslashes($given_password));
				$sql = "UPDATE exp_members SET password = '$encrypted_password' WHERE username = '$given_username'";
				$this->debug_print("Updating user with SQL: $sql");				
				$DB->query($sql);
			
				if ($this->settings['use_ldap_account_creation']) // now we might want to do some EE account creation...
				{
					$sql = "SELECT * FROM exp_members WHERE username = '$given_username'";					
					$query = $DB->query($sql);

					$account_exists = (sizeof($query->row) > 0);
					if (!$account_exists) // user doesn't exist in exp_members table, so we will create an EE account
					{
						$this->debug_print("Using LDAP for account creation...");
						$unique_id = $FNS->random('encrypt');
						$join_date = $LOC->now;
						
						$sql = "INSERT INTO exp_members SET ".
							   "username = '$given_username', ".
							   "password = '$encrypted_password', ".
							   "unique_id = '$unique_id', ".
							   "group_id = '6', ".
							   "screen_name = '{$user_info['cn'][0]}', ".
							   "email = '{$user_info['mail'][0]}', ".
							   "ip_address = '0.0.0.0', ".
							   "join_date = '$join_date', ".
							   "language = 'english', ".
							   "timezone = 'UTC', ".
							   "time_format = 'eu'";
						
						$this->debug_print("Inserting user with SQL: $sql");
						$query = $DB->query($sql);	   
						
						$member_id = $DB->insert_id;
						if ($member_id > 0) // update other relevant fields
						{
							$sql = "UPDATE exp_members SET photo_filename = 'photo_$member_id.jpg', photo_width = '90', photo_height = '120'";
							$query = $DB->query($sql);
							
							$DB->query("INSERT INTO exp_member_data SET member_id = $member_id");
							$DB->query("INSERT INTO exp_member_homepage SET member_id = $member_id");							
							
							$STAT->update_member_stats();
							
							ini_set('SMTP', $this->settings['mail_host']);
							$headers = 'From: noreply@ncsoft.com' . "\r\n" .
									   'X-Mailer: PHP/' . phpversion();
							$success = mail($this->settings['admin_email'], 
											"A new member, '$given_username', has been registered on http://{$_SERVER['HTTP_HOST']}", 
											"This is a message from the ExpressionEngine LDAP authentication system.\n\nPlease log in to http://{$_SERVER['HTTP_HOST']} and update their member group, profile and 'people' weblog entry for user, '$given_username'.",
											$headers); 
						}
						else
						{
							exit("Could not create user account for $given_username<br/>\n");
						}
					}
				}						
			}
			else
			{
				$this->debug_print("Could not authenticate username '$given_username' with LDAP");
			}
			
			$this->debug_print("Closing connection...");
			ldap_close($conn) or die("Could not close the LDAP connection<br/>\n");			
		}
		
		if ($this->debug)
		{
			exit($output);
		}
			
		return $output;
	}
	
	function debug_print($message, $br="<br/>\n")
	{
		if ($this->debug)
		{
			if (is_array($message))
			{
				print("<pre>");
				print_r($message);
				print("</pre>$br");
			}
			else
			{
				print("$message $br");
			}
		}
	}
}


?>